package biz

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"gitee.com/shuokeyun/kratos/errors"
	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/proto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"io"
	"strings"
	// Register gzip compressor so compressed responses will work
	_ "google.golang.org/grpc/encoding/gzip"
	// Register xds so xds and xds-experimental resolver schemes work
	_ "google.golang.org/grpc/xds"

	"github.com/fullstorydev/grpcurl"
)

var (
	addlHeaders multiString
	rpcHeaders  multiString
)

type multiString []string

func (s *multiString) String() string {
	return strings.Join(*s, ",")
}

func (s *multiString) Set(value string) error {
	*s = append(*s, value)
	return nil
}

type grpcCurl struct {
	source grpcurl.DescriptorSource
}

func (g *grpcCurl) InvokeRPC(ctx context.Context, cc *grpc.ClientConn, symbol string, request []byte) (interface{}, error) {
	var input rpcInput
	var err error
	if err = json.Unmarshal(request, &input); err != nil {
		return nil, err
	}

	reqStats := rpcRequestStats{
		Total: len(input.Data),
	}
	requestFunc := func(m proto.Message) error {
		if len(input.Data) == 0 {
			return io.EOF
		}
		reqStats.Sent++
		req := input.Data[0]
		input.Data = input.Data[1:]
		if err := jsonpb.Unmarshal(bytes.NewReader([]byte(req)), m); err != nil {
			return status.Errorf(codes.InvalidArgument, err.Error())
		}
		return nil
	}
	h := rpcResult{
		descSource: g.source,
		Requests:   &reqStats,
	}

	err = grpcurl.InvokeRPC(ctx, g.source, cc, symbol, append(addlHeaders, rpcHeaders...), &h, requestFunc)
	if err != nil {
		if errStatus, ok := status.FromError(err); ok {
			fmt.Println(errStatus)
		} else {
			return nil, errors.NewWrap(err, 500, "INTERNAL", fmt.Sprintf("Error invoking method %q", symbol))
		}
	}
	if h.Error != nil {
		return nil, errors.New(int(h.Error.Code), h.Error.Name, h.Error.Message)
	}
	return h.Responses, nil
}
